import os
import random
import pandas as pd
from django.contrib import messages
from django.http import HttpRequest
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "MusicRecommendSystem.settings")
django.setup()

from django.contrib.auth.models import User
from music.models import UserProfile, Music

current_request = None

"""
由于无法安装surprise库，我们使用一个简单的基于用户相似度的推荐算法替代SVD算法。
这个简单算法的工作原理是:
1. 找到与当前用户喜欢类似音乐的其他用户
2. 推荐那些相似用户喜欢但当前用户尚未评价的音乐
"""

# 获取数据库中所有用户数据
def build_df():
    data = []
    for user_profile in UserProfile.objects.all():  # 获取所有的用户信息
        for like_music in user_profile.likes.all():  # 循环获取所有用户喜欢的歌曲
            data.append([user_profile.user.id, like_music.pk, 1])  # 保存数据data = [[1,1,1],]
        for dislike_music in user_profile.dislikes.all():  # 循环获取所有用户不喜欢的歌曲
            data.append([user_profile.user.id, dislike_music.pk, 0])  # 保存数据data = [[1,2,0]]

    return pd.DataFrame(data, columns=['userID', 'itemID', 'rating'])  # 存储格式


def build_predictions(df: pd.DataFrame, user: User):
    """使用简化版本的推荐算法，基于用户相似度"""
    userId = user.id  # 获取用户的ID
    profile = UserProfile.objects.filter(user=user)  # 查找用户的个人资料信息
    if profile.exists():
        profile_obj: UserProfile = profile.first()
    else:
        return []
    
    # 获取当前用户的喜好和不喜好
    user_like = list(profile_obj.likes.all().values_list('id', flat=True))
    user_dislike = list(profile_obj.dislikes.all().values_list('id', flat=True))
    
    # 如果用户没有评价过任何歌曲，则无法进行推荐
    if not user_like:
        return []
    
    # 推荐结果集
    result_set = []
    
    # 从数据集中找出喜欢当前用户也喜欢的歌曲的其他用户
    similar_users = set()
    for music_id in user_like:
        # 找到也喜欢这首歌的其他用户
        similar_df = df[(df['itemID'] == music_id) & (df['rating'] == 1) & (df['userID'] != userId)]
        similar_users.update(similar_df['userID'].tolist())
    
    # 如果找不到相似用户，返回空列表
    if not similar_users:
        return []
    
    # 从相似用户喜欢的歌曲中筛选出当前用户尚未评价的歌曲
    potential_recommendations = set()
    for sim_user in similar_users:
        liked_df = df[(df['userID'] == sim_user) & (df['rating'] == 1)]
        potential_recommendations.update(liked_df['itemID'].tolist())
    
    # 排除当前用户已评价的歌曲
    potential_recommendations = potential_recommendations - set(user_like + user_dislike)
    
    # 从潜在推荐歌曲中选择最多10首
    if potential_recommendations:
        items_to_recommend = list(potential_recommendations)
        if len(items_to_recommend) > 10:
            items_to_recommend = random.sample(items_to_recommend, 10)
        
        # 获取这些歌曲的详细信息
        for music_id in items_to_recommend:
            try:
                music = Music.objects.get(pk=music_id)
                result_set.append(music)
            except Music.DoesNotExist:
                continue
    
    # 如果没有推荐，显示消息
    if len(result_set) == 0 and current_request:
        messages.error(current_request, '你听的歌太少了，多听点歌再来吧~')
    
    return result_set


# 获取用户流派推荐
def build_genre_predictions(user: User):
    predictions = []
    profile = UserProfile.objects.filter(user=user)  # 用户信息
    if profile.exists():
        profile_obj: UserProfile = profile.first()
    else:
        return predictions

    genre_subscribe = profile_obj.genre_subscribe.split(',')  # 获取用户订阅的流派
    user_like = profile_obj.likes.all()  # 获取用户喜欢的音乐
    user_dislike = profile_obj.dislikes.all()  # 获取用户不喜欢的音乐

    # 查找遍历用户喜欢流派的所有音乐
    for music in Music.objects.filter(genre_ids__in=genre_subscribe):
        if music in user_like:
            continue
        if music in user_dislike:
            continue
        predictions.append(music)

    return predictions


# 构建语言推荐
def build_language_predictions(user: User):
    predictions = []
    profile = UserProfile.objects.filter(user=user)
    if profile.exists():
        profile_obj: UserProfile = profile.first()
    else:
        return predictions

    language_subscribe = profile_obj.language_subscribe.split(',')  # 获取用户喜欢的语言
    user_like = profile_obj.likes.all()
    user_dislike = profile_obj.dislikes.all()

    for music in Music.objects.filter(language__in=language_subscribe):
        if music in user_like:
            continue
        if music in user_dislike:
            continue
        predictions.append(music)

    return predictions


# 构建推荐
def build_recommend(request: HttpRequest, user: User):
    global current_request
    current_request = request
    predictions = []
    predictions.extend(build_predictions(build_df(), user))  # 算法预测
    if not predictions:
        predictions.extend(build_genre_predictions(user))  # 流派推荐
        predictions.extend(build_language_predictions(user))  # 语言推荐
    return predictions


if __name__ == '__main__':
    # print(build_df())  # 获取用户数据
    print(build_predictions(build_df(), User.objects.get(pk=4)))  # 算法推荐
    print(build_genre_predictions(User.objects.get(pk=4)))  # 流派推荐
    print(build_language_predictions(User.objects.get(pk=4)))  # 语言推荐
